﻿Imports System.Text
Imports System.Reflection
Imports System.IO
Imports System.Runtime.Serialization.Formatters.Binary

Namespace Util

    ''' <summary>
    ''' 共通ユーティリティクラス。
    ''' </summary>
    ''' <history>[sonodar] 2011/07/06 グラビアからコピーして新規作成</history>
    Public NotInheritable Class CommonUtil

        ''' <summary>
        ''' デフォルトコンストラクタを不可視とすることで、<br/>
        ''' インスタンスの生成を禁止する。
        ''' </summary>
        Private Sub New()
            '// 何もしない    
        End Sub

        ''' <summary>
        ''' すべての引数が空文字またはNothingだった場合にTrueを返す。<br/>
        ''' 引数のうちひとつでも空でない要素があった場合はFalse。
        ''' </summary>
        ''' <param name="args">オブジェクト</param>
        ''' <returns>すべての引数が空文字またはNothingだった場合にTrue</returns>
        <System.Diagnostics.DebuggerStepThrough()> _
        Public Shared Function IsEmpty(ByVal ParamArray args() As Object) As Boolean
            If (args Is Nothing OrElse args.Length = 0) Then
                Return True
            End If
            For Each s In args
                If (s Is Nothing OrElse s.Equals(String.Empty)) Then
                    Continue For
                End If
                Return False
            Next
            Return True
        End Function

        ''' <summary>
        ''' すべての引数が空文字またはNothingでなかった場合にTrueを返す。<br/>
        ''' ひとつでも空の要素があった場合はFalse。
        ''' </summary>
        ''' <param name="args">オブジェクト</param>
        ''' <returns>すべての引数が空文字またはNothingでなかった場合にTrue</returns>
        <System.Diagnostics.DebuggerStepThrough()> _
        Public Shared Function IsNotEmpty(ByVal ParamArray args() As Object) As Boolean
            If (args Is Nothing OrElse args.Length = 0) Then
                Return False
            End If
            For Each s In args
                If (s Is Nothing OrElse s.Equals(String.Empty)) Then
                    Return False
                End If
            Next
            Return True
        End Function

        ''' <summary>
        ''' オブジェクトが指定された範囲内かどうかをチェックする。
        ''' </summary>
        ''' <typeparam name="T">IComparableを実装したオブジェクト</typeparam>
        ''' <param name="obj">範囲をチェックするオブジェクト</param>
        ''' <param name="min">範囲の最小値(Nothingを指定した場合は無制限)</param>
        ''' <param name="max">範囲の最大値(Nothingを指定した場合は無制限)</param>
        ''' <param name="equalsCase">境界値をエラーとするかどうか</param>
        ''' <returns>範囲内ならFalse、範囲外ならTrue</returns>
        ''' <remarks>
        ''' min, maxにNothingが指定された場合は、それぞれ無制限となる。<br/>
        ''' min, maxともNothingだった場合、結果は常にTrueになる。
        ''' </remarks>
        Public Overloads Shared Function IsRange(Of T As IComparable)(ByVal obj As T, ByVal min As T, ByVal max As T, _
                                                            Optional ByVal equalsCase As Boolean = False) As Boolean
            Dim compare As Integer  '// 比較結果

            '// 最小値が指定されていた場合
            If (min IsNot Nothing) Then

                compare = obj.CompareTo(min)

                '// EqualsCaseがTrueの場合は同値をエラーとする
                If (equalsCase AndAlso compare = 0) Then
                    Return False
                End If

                '// 最小値より小さければNG
                If (compare < 0) Then
                    Return False
                End If

            End If

            '// 最大値が指定されていた場合
            If (max IsNot Nothing) Then

                compare = obj.CompareTo(max)

                '// EqualsCaseがTrueの場合は同値をエラーとする
                If (equalsCase AndAlso compare = 0) Then
                    Return False
                End If

                '// 最大値より大きければNG
                If (compare > 0) Then
                    Return False
                End If

            End If

            Return True

        End Function

        ''' <summary>
        ''' 値型の値が指定された範囲内かどうかをチェックする。
        ''' </summary>
        ''' <typeparam name="T">IComparableを実装した値型の値</typeparam>
        ''' <param name="obj">範囲をチェックするオブジェクト</param>
        ''' <param name="min">範囲の最小値(Nothingを指定した場合は無制限)</param>
        ''' <param name="max">範囲の最大値(Nothingを指定した場合は無制限)</param>
        ''' <param name="equalsCase">境界値をエラーとするかどうか</param>
        ''' <returns>範囲内ならFalse、範囲外ならTrue</returns>
        ''' <remarks>
        ''' min, maxにNothingが指定された場合は、それぞれ無制限となる。<br/>
        ''' min, maxともNothingだった場合、結果は常にTrueになる。
        ''' </remarks>
        Public Overloads Shared Function IsRange(Of T As Structure)(ByVal obj As T, ByVal min As Nullable(Of T), _
                                                                    ByVal max As Nullable(Of T), _
                                                                    Optional ByVal equalsCase As Boolean = False) As Boolean
            Dim compare As Integer  '// 比較結果

            '// 最小値が指定されていた場合
            If (min IsNot Nothing) Then

                compare = DirectCast(obj, IComparable).CompareTo(min)

                '// EqualsCaseがTrueの場合は同値をエラーとする
                If (equalsCase AndAlso compare = 0) Then
                    Return False
                End If

                '// 最小値より小さければNG
                If (compare < 0) Then
                    Return False
                End If

            End If

            '// 最大値が指定されていた場合
            If (max IsNot Nothing) Then

                compare = DirectCast(obj, IComparable).CompareTo(max)

                '// EqualsCaseがTrueの場合は同値をエラーとする
                If (equalsCase AndAlso compare = 0) Then
                    Return False
                End If

                '// 最大値より大きければNG
                If (compare > 0) Then
                    Return False
                End If

            End If

            Return True

        End Function

        ''' <summary>
        ''' objがNothingだった場合に、replaceを返す。<br/>
        ''' Nothingでなければobjを返す。
        ''' </summary>
        ''' <param name="obj">オブジェクト</param>
        ''' <param name="replace">補完するオブジェクト</param>
        ''' <returns>objがNothingだった場合はreplace</returns>
        <System.Diagnostics.DebuggerStepThrough()> _
        Public Overloads Shared Function NullIf(Of T)(ByVal obj As T, ByVal replace As T) As T
            If (obj IsNot Nothing) Then
                Return obj
            End If
            If (replace Is Nothing) Then
                Throw New NullReferenceException("置き換えるオブジェクトがNULLを参照しています。")
            End If
            Return replace
        End Function

        ''' <summary>
        ''' expressionがTrueだった場合に、trueValueを返す。<br/>
        ''' Falseだった場合は、falseValueを返す。
        ''' </summary>
        ''' <param name="expression">評価式</param>
        ''' <param name="trueValue">Trueの場合に返すオブジェクト</param>
        ''' <param name="falseValue">Falseの場合に返すオブジェクト</param>
        ''' <returns>expressionがTrueだった場合はtrueValue、そうでなければfalseValue</returns>
        <System.Diagnostics.DebuggerStepThrough()> _
        Public Overloads Shared Function IfValue(Of T)(ByVal expression As Boolean, ByVal trueValue As T, ByVal falseValue As T) As T
            If (expression) Then
                Return trueValue
            End If
            Return falseValue
        End Function

        ''' <summary>
        ''' 第一引数の値が、第二引数以降の値のいずれかと一致したらTrueを返す。<br/>
        ''' すべての引数と一致しなかった場合のみFalseを返す。
        ''' </summary>
        ''' <param name="src">比較基準値</param>
        ''' <param name="targets">比較値</param>
        Public Shared Function EqualsAny(ByVal src As Object, ByVal ParamArray targets() As Object) As Boolean

            '// 値がNothingの場合
            If (src Is Nothing) Then

                '// 比較値がなければTrue
                If (CollectionUtil.IsEmpty(targets)) Then
                    Return True
                End If

                '// 比較値のいずれかがNothingだったらTrue
                For Each target As Object In targets
                    If (target Is Nothing) Then
                        Return True
                    End If
                Next

                Return False
            End If

            '// 比較値がなければFalse
            If (CollectionUtil.IsEmpty(targets)) Then
                Return False
            End If

            '// 1つでも一致したらTrue
            For Each target As Object In targets
                If (src.Equals(target)) Then
                    Return True
                End If
            Next

            Return False

        End Function

        ''' <summary>
        ''' オブジェクトのクローンを生成する。<br/>
        ''' ただし、デフォルトコンストラクタが定義されているクラスのみ。
        ''' </summary>
        ''' <param name="obj">デフォルトコンストラクタが定義されているクラスのインスタンス</param>
        ''' <returns>クローニングされたインスタンス</returns>
        Public Shared Function Clone(Of T)(ByVal obj As T) As T
            Dim ret As T = DirectCast(Activator.CreateInstance(obj.GetType), T)
            ReflectionUtil.CopyProperties(obj, ret)
            Return ret
        End Function

        ''' <summary>
        ''' Nullable(Of Integer)のインスタンスがNothingだったら0を返し、<br/>
        ''' Nothingでなければ値を返す。
        ''' </summary>
        Public Overloads Shared Function NullZero(ByVal num? As Integer) As Integer
            If (num Is Nothing) Then
                Return 0
            End If
            Return num.Value
        End Function

        ''' <summary>
        ''' Nullable(Of Long)のインスタンスがNothingだったら0を返し、<br/>
        ''' Nothingでなければ値を返す。
        ''' </summary>
        Public Overloads Shared Function NullZero(ByVal num? As Long) As Long
            If (num Is Nothing) Then
                Return 0
            End If
            Return num.Value
        End Function

        ''' <summary>
        ''' Nullable(Of Single)のインスタンスがNothingだったら0を返し、<br/>
        ''' Nothingでなければ値を返す。
        ''' </summary>
        Public Overloads Shared Function NullZero(ByVal num? As Single) As Single
            If (num Is Nothing) Then
                Return 0
            End If
            Return num.Value
        End Function

        ''' <summary>
        ''' Nullable(Of Double)のインスタンスがNothingだったら0を返し、<br/>
        ''' Nothingでなければ値を返す。
        ''' </summary>
        Public Overloads Shared Function NullZero(ByVal num? As Double) As Double
            If (num Is Nothing) Then
                Return 0
            End If
            Return num.Value
        End Function

        ''' <summary>
        ''' Nullable(Of Decimal)のインスタンスがNothingだったら0を返し、<br/>
        ''' Nothingでなければ値を返す。
        ''' </summary>
        Public Overloads Shared Function NullZero(ByVal num? As Decimal) As Decimal
            If (num Is Nothing) Then
                Return 0
            End If
            Return num.Value
        End Function

        ''' <summary>
        ''' Nullable(Of Byte)のインスタンスがNothingだったら0を返し、<br/>
        ''' Nothingでなければ値を返す。
        ''' </summary>
        Public Overloads Shared Function NullZero(ByVal num? As Byte) As Byte
            If (num Is Nothing) Then
                Return 0
            End If
            Return num.Value
        End Function

        ''' <summary>
        ''' Nullable(Of Short)のインスタンスがNothingだったら0を返し、<br/>
        ''' Nothingでなければ値を返す。
        ''' </summary>
        Public Overloads Shared Function NullZero(ByVal num? As Short) As Short
            If (num Is Nothing) Then
                Return 0
            End If
            Return num.Value
        End Function

        ''' <summary>
        ''' Nullable(Of Boolean)のインスタンスがNothingだったらFalseを返し、<br/>
        ''' Nothingでなければ値を返す。
        ''' </summary>
        Public Shared Function NullFalse(ByVal num? As Boolean) As Boolean
            If (num Is Nothing) Then
                Return False
            End If
            Return num.Value
        End Function

        ''' <summary>
        ''' オブジェクトのディープコピーを生成して返す。(シリアライズ可能なクラスのみ)
        ''' </summary>
        ''' <typeparam name="T">ディープコピーを生成するオブジェクトの総称型。シリアライズ可能なクラスのみ。</typeparam>
        ''' <param name="Obj">ディープコピーを生成するオブジェクト。シリアライズ可能なクラスのみ。</param>
        ''' <returns>Objをディープコピーしたインスタンス</returns>
        Public Shared Function DeepCopy(Of T)(ByVal Obj As T) As T

            Dim copy As T = Nothing

            Dim Formatter As New BinaryFormatter()

            Using Input As New MemoryStream()

                '// コピー元オブジェクトをシリアライズ
                Formatter.Serialize(Input, Obj)

                '// カーソルを先頭に移動
                Input.Position = 0

                '// オブジェクトをデシリアライズ
                copy = DirectCast(Formatter.Deserialize(Input), T)

            End Using

            Return copy

        End Function

    End Class

End Namespace